fix: actionable error for Alembic-blind schema drift#99
Merged
Conversation
validate_schema() detects CHECK/partial-index drift that `alembic revision --autogenerate` cannot remediate. Spec appends a docs-pointer to the RuntimeError (only when blind drift is present) plus a hand-written-migration recipe in docs/operations/alembic.md. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… hint Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
… drift Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…in recipe Final-review polish: cover the index/check predicate-drift blind path at the integration level too, and warn operators with a SQLAlchemy naming_convention to wrap recipe identifiers in op.f(). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
…d in #99) Move the change bundle active → archive with status: shipped / pr: 99, add the README index entry, and promote the conclusion into CLAUDE.md's User-owned schema section (validate_schema now appends a remediation pointer for Alembic-blind drift). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
lesnik512
added a commit
that referenced
this pull request
Jun 16, 2026
A MetaData carrying a SQLAlchemy `ck` naming_convention re-templates the package's explicitly-named lease CheckConstraint (the given name fills the %(constraint_name)s token), so the live constraint is named e.g. `ck_<table>_<table>_lease_ck`, not `<table>_lease_ck`. The CHECK probe hard-coded the literal name, never found the re-templated one, and raised a spurious "missing CHECK constraint" on a valid schema for every deployment using the SQLAlchemy/Alembic-recommended convention. _validate_check_constraints_sync now derives the expected name from the Table object via _resolve_check_constraint_name (identify the lease constraint by its normalized predicate, use its convention-resolved .name), falling back to the literal name only when the table carries no matching constraint. Explicitly named indexes are unaffected (ix/uq keys only re-template auto-named indexes). - tests: convention-resolved name honored, missing-under-convention, literal fallback (unit), and an end-to-end Postgres pass under a ck convention. - docs/operations/alembic.md: replace the misleading op.f(literal) caveat with an introspect-the-rendered-name recipe. - architecture/dlq.md: record the convention-awareness invariant. - planning/releases/0.10.2.md: release notes. - planning: remove stale draft duplicate of the shipped #99 bundle from active/ (canonical copy already in archive/). Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
OutboxClient.validate_schema()detects schema drift thatalembic revision --autogeneratecannot generate a remediation migration for — a missing/alteredoutbox_lease_ckCHECK constraint and partial-index predicate drift. Previously the raisedRuntimeErrorwas a dead end: re-running autogenerate produced an emptyupgrade(). Now, when an Alembic-blind drift is present, the error appends a pointer to a new docs section holding the exact hand-writtenop.*migration recipe.What changed
client.py— new module-level_SCHEMA_MISMATCH_PREFIX/_AUTOGEN_BLIND_HINTand a pure helper_compose_schema_mismatch_message(errors, *, has_blind_drift).validate_schema()collects the two Alembic-blind probes (_validate_index_predicates_sync,_validate_check_constraints_sync) into ablind_errorslist and appends the pointer only when one of them fired. The error prefix and per-error strings are byte-for-byte unchanged → existing greps /pytest match=still hold; autogenerate-fixable drift (columns, plain indexes, DLQ) gets no pointer.docs/operations/alembic.md#fixing-drift-autogenerate-cant-seesection (CHECK + partial-index recipes, with anaming_convention/op.f()caveat) and a cross-link fromdocs/usage/schema-validation.md.Verification
just lint-ciclean (ruff format, ruff check, ty, eof-fixer)mkdocs build --strictcleanDesign + plan:
planning/changes/archive/2026-06-16.01-actionable-schema-drift-error/.🤖 Generated with Claude Code